package com.twj.tools.parse;

import java.sql.Timestamp;
import java.time.*;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.time.temporal.TemporalAccessor;
import java.time.temporal.TemporalQueries;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author 作者: tianwenju
 * @version 1.0
 * @project 金税三期工程核心征管及应用总集成项目
 * @package com.twj.tools.parse
 * @file DateUtils.java 创建时间:2023年07月14日 10:24
 * @title 标题（要求能简洁地表达出类的功能和职责）
 * @description 描述（简要描述类的职责、实现方式、使用注意事项等）
 * @copyright Copyright (c) 2020 中国软件与技术服务股份有限公司
 * @company 中国软件与技术服务股份有限公司
 * @module 模块: 模块名称
 * @reviewer 审核人:
 * @history 修订历史（历次修订内容、修订人、修订时间等）
 */
public class DateUtils {
    public static final String[] dateFormat = new String[]{"yyyy-MM-dd HH:mm:ss", "yyyy/MM/dd HH:mm:ss", "yyyy年MM月dd日HH时mm分ss秒", "yyyy-MM-dd", "yyyy/MM/dd", "yy-MM-dd", "yy/MM/dd", "yyyy年MM月dd日", "HH:mm:ss", "yyyyMMddHHmmss", "yyyyMMdd", "yyyy.MM.dd", "yy.MM.dd", "yyyyMMddHHmmssSSS", "yyyy-MM-dd HH:mm:ss:SSS", "yyyy-MM-dd HH:mm:ss.SSS", "yyyy", "yyyyMM", "yyyyMMdd HH", "yyyyMMdd HH:mm", "yyyyMMdd HH:mm:ss"};
    private static final Map<String, DateTimeFormatter> dateFormatersCache = new ConcurrentHashMap();
    private static final ZoneId systemDefaultZoneId = ZoneId.systemDefault();
    private static final LocalDate localDate19700101;

    public DateUtils() {
    }

    public static Calendar getSystemCurrentTime() {
        return Calendar.getInstance();
    }

    public static String getSystemCurrentTime(int format) {
        return toDateStrByFormatIndex(getSystemCurrentTime(), format);
    }

    public static String getSystemCurrentTime(String format) {
        return toDateStrByFormat(getSystemCurrentTime(), format);
    }

    private static DateTimeFormatter getDateFormater(String format) {
        DateTimeFormatter dateTimeFormatter = (DateTimeFormatter) dateFormatersCache.get(format);
        if (dateTimeFormatter == null) {
            dateTimeFormatter = DateTimeFormatter.ofPattern(format);
            dateFormatersCache.putIfAbsent(format, dateTimeFormatter);
        }

        return dateTimeFormatter;
    }

    public static String toDateStrByFormat(Calendar date, String format) {
        return date == null ? null : getDateFormater(format).format(toLocalDateTime(date));
    }

    public static String toDateStrByFormatIndex(Calendar date, int format) {
        return toDateStrByFormat(date, dateFormat[format]);
    }

    public static String toDateStrByFormat(Date date, String format) {
        return date == null ? null : getDateFormater(format).format(toZonedDateTime(date));
    }

    public static String toDateStrByFormatIndex(Date date, int format) {
        return toDateStrByFormat(date, dateFormat[format]);
    }

    public static String toDateTimeStr(Calendar date) {
        return toDateStrByFormatIndex((Calendar) date, 0);
    }

    public static String toDateTimeStr(int format, Calendar date) {
        return toDateStrByFormatIndex(date, format);
    }

    public static String toDateStr(Calendar date) {
        return toDateStrByFormatIndex((Calendar) date, 3);
    }

    public static String dateToString(Date date, int format) {
        return toDateStrByFormatIndex(date, format);
    }

    public static String dateToString(Date date) {
        return toDateStrByFormatIndex((Date) date, 3);
    }

    public static Date convertCalendarToDate(Calendar c) {
        Date d = new Date();
        d.setTime(c.getTimeInMillis());
        return d;
    }

    public static Date StringToDate(String dateStr) {
        return parseDate(dateStr, 0);
    }

    public static Date parseDate(String dateStr, int format) {
        if (dateStr != null && dateStr.length() != 0) {
            try {
                DateTimeFormatter pattern = getDateFormater(dateFormat[format]);
                TemporalAccessor ta = pattern.parse(dateStr);
                return toDate(ta);
            } catch (DateTimeParseException var4) {
                return loopParseDate(dateStr, 0, dateFormat.length - 1);
            } catch (ArrayIndexOutOfBoundsException var5) {
                //throw new Exception("UT-07001:日期字符串" + dateStr + "格式不支持", var5);
            }
        }
        return null;
    }

    private static Date loopParseDate(String dateStr, int startIdx, int endIdx) {
        if (dateStr != null && dateStr.length() != 0) {
            Date date = null;

            for (int i = startIdx; i <= endIdx; ++i) {
                try {
                    DateTimeFormatter pattern = getDateFormater(dateFormat[i]);
                    TemporalAccessor ta = pattern.parse(dateStr);
                    date = toDate(ta);
                } catch (DateTimeParseException var7) {
                }
            }

            if (date == null) {
//                throw new SwordBaseCheckedException("UT-07001:日期字符串" + dateStr + "格式不支持");
            } else {
                return date;
            }
        }
        return null;
    }

    public static Date parseDate(String dateStr, String dateFormat) {
        if (dateStr != null && dateStr.length() != 0) {
            try {
                DateTimeFormatter pattern = getDateFormater(dateFormat);
                TemporalAccessor ta = pattern.parse(dateStr);
                return toDate(ta);
            } catch (DateTimeParseException var4) {
//                throw new Exception("UT-07001:日期字符串" + dateStr + "按格式" + dateFormat + "解析出错", var4);
            }
        }
        return null;
    }

    private static Date toDate(TemporalAccessor ta) {
        LocalDate ld = (LocalDate) ta.query(TemporalQueries.localDate());
        LocalTime lt = (LocalTime) ta.query(TemporalQueries.localTime());
        LocalDateTime ldt = null;
        if (ld != null && lt != null) {
            ldt = ld.atTime(lt);
        } else if (ld != null) {
            ldt = ld.atStartOfDay();
        } else if (lt != null) {
            ldt = localDate19700101.atTime(lt);
        }

        Instant instant = ldt.atZone(systemDefaultZoneId).toInstant();
        return Date.from(instant);
    }

    public static Calendar convUtilDateToUtilCalendar(Date date) {
        if (date == null) {
            return null;
        } else {
            GregorianCalendar gc = new GregorianCalendar();
            gc.setTimeInMillis(date.getTime());
            return gc;
        }
    }

    public static Calendar convSqlTimestampToUtilCalendar(Timestamp date) {
        if (date == null) {
            return null;
        } else {
            GregorianCalendar gc = new GregorianCalendar();
            gc.setTimeInMillis(date.getTime());
            return gc;
        }
    }

    public static Calendar parseDate(String dateStr) {
        Date result = parseDate(dateStr, 0);
        Calendar cal = null;
        if (result != null) {
            cal = new GregorianCalendar(0, 0, 0, 0, 0, 0);
            cal.setTime(result);
        }

        return cal;
    }

    public static Timestamp convUtilDateToSqlTimestamp(Date date) {
        return date == null ? null : new Timestamp(date.getTime());
    }

    public static Timestamp convUtilCalendarToSqlTimestamp(Calendar date) {
        return date == null ? null : new Timestamp(date.getTimeInMillis());
    }

    public static Timestamp parseTimestamp(Calendar calendar) {
        return new Timestamp(calendar.getTimeInMillis());
    }

    public static Timestamp parseTimestamp(String dateStr) {
        return parseTimestamp(dateStr, 3);
    }

    public static Timestamp parseTimestamp(String dateStr, int format) {
        try {
            DateTimeFormatter pattern = getDateFormater(dateFormat[format]);
            LocalDate dt = LocalDate.parse(dateStr, pattern);
            return Timestamp.valueOf(dt.atStartOfDay());
        } catch (DateTimeParseException var5) {
            Date date = parseDate(dateStr, 0);
            LocalDate ld = toLocalDate(date);
            return new Timestamp(toMillSeconds(ld));
        }
    }

    public static int calendarMinus(Calendar d1, Calendar d2) {
        if (d1 != null && d2 != null) {
            LocalDateTime t1 = LocalDateTime.of(d1.get(1), d1.get(2) + 1, d1.get(5), 0, 0);
            LocalDateTime t2 = LocalDateTime.of(d2.get(1), d2.get(2) + 1, d2.get(5), 0, 0);
            Duration duration = Duration.between(t2, t1);
            return (int) duration.toDays();
        } else {
            return 0;
        }
    }

    public static long calendarminus(Calendar d1, Calendar d2) {
        return d1 != null && d2 != null ? (d1.getTimeInMillis() - d2.getTimeInMillis()) / 86400000L : 0L;
    }

    public static int calcMonthDays(Calendar date) {
        return date.getActualMaximum(5);
    }

    private static int calcDays(long t1, long t2) {
        long millis = t1 - t2;
        return millis == 0L ? 0 : (int) (millis / 86400000L);
    }

    public static int calcDays(Calendar c1, Calendar c2) {
        return calcDays(c1.getTimeInMillis(), c2.getTimeInMillis());
    }

    public static int calcDays(Date d1, Date d2) {
        return calcDays(d1.getTime(), d2.getTime());
    }


    public static Calendar lastDay(Calendar c) {
        Calendar t = (Calendar) c.clone();
        t.set(5, 1);
        t.add(2, 1);
        t.add(5, -1);
        return t;
    }

    public static Calendar lastDay(String dateStr) {
        Calendar t = parseDate(dateStr);
        t.set(5, 1);
        t.add(2, 1);
        t.add(5, -1);
        return t;
    }

    public static Calendar[] calcAQuarter(Calendar day) {
        LocalDate date = LocalDate.of(day.get(1), day.get(2) + 1, day.get(5));
        Month month = date.getMonth();
        Month firstMonthOfQuarter = month.firstMonthOfQuarter();
        Month lastMonthOfQuarter = Month.of(firstMonthOfQuarter.getValue() + 2);
        boolean leapYear = date.isLeapYear();
        LocalDate first = LocalDate.ofYearDay(date.getYear(), firstMonthOfQuarter.firstDayOfYear(leapYear));
        LocalDate last = LocalDate.of(date.getYear(), lastMonthOfQuarter, lastMonthOfQuarter.length(leapYear));
        Calendar[] quarter = new Calendar[]{Calendar.getInstance(), null};
        quarter[0].set(1, first.getYear());
        quarter[0].set(2, first.getMonthValue() - 1);
        quarter[0].set(5, first.getDayOfMonth());
        quarter[1] = Calendar.getInstance();
        quarter[1].set(1, last.getYear());
        quarter[1].set(2, last.getMonthValue() - 1);
        quarter[1].set(5, last.getDayOfMonth());
        return quarter;
    }

    public static int[] getYearMonthDayHH24MiMM(Calendar calendar) {
        return new int[]{calendar.get(1), calendar.get(2) + 1, calendar.get(5), calendar.get(11), calendar.get(12), calendar.get(13), calendar.get(14)};
    }

    public static int[] getYearMonthDayHH24MiMM(Date date) {
        Calendar calendar = getSystemCurrentTime();
        calendar.setTime(date);
        return getYearMonthDayHH24MiMM(calendar);
    }

    public static long nsToMs(long nsTime) {
        return nsTime / 1000000L;
    }

    public static LocalDateTime toLocalDateTime(Calendar cal) {
        return LocalDateTime.ofInstant(cal.toInstant(), cal.getTimeZone().toZoneId());
    }

    public static LocalDateTime toLocalDateTime(Date date) {
        return LocalDateTime.ofInstant(date.toInstant(), systemDefaultZoneId);
    }

    public static LocalDate toLocalDate(Calendar cal) {
        return LocalDate.of(cal.get(1), cal.get(2) + 1, cal.get(5));
    }

    public static LocalDate toLocalDate(Date date) {
        return date.toInstant().atZone(systemDefaultZoneId).toLocalDate();
    }

    public static LocalDate toLocalDate(java.sql.Date date) {
        return date.toLocalDate();
    }

    public static ZonedDateTime toZonedDateTime(java.sql.Date date) {
        return toZonedDateTime(date.getTime());
    }

    public static ZonedDateTime toZonedDateTime(Date date) {
        return toZonedDateTime(date.getTime());
    }

    public static ZonedDateTime toZonedDateTime(long millseconds) {
        Instant instant = Instant.ofEpochMilli(millseconds);
        return instant.atZone(systemDefaultZoneId);
    }

    public static long toMillSeconds(LocalDateTime localDateTime) {
        return localDateTime.atZone(systemDefaultZoneId).toInstant().toEpochMilli();
    }

    public static long toMillSeconds(ZonedDateTime zonedDateTime) {
        return zonedDateTime.toInstant().toEpochMilli();
    }

    public static long toMillSeconds(LocalDate localDate) {
        LocalDateTime ldt = localDate.atStartOfDay();
        return toMillSeconds(ldt);
    }

    public static void main(String[] args) {
        Calendar time = getSystemCurrentTime();

        for (int i = 0; i < dateFormat.length; ++i) {
            System.out.println(i + "[" + dateFormat[i] + "] -> " + toDateStrByFormatIndex(time, i));
        }

    }

    static {
        localDate19700101 = LocalDate.of(1970, Month.JANUARY, 1);
    }
}
